package ru.classbase.dbdumper.storage

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport
import com.google.api.client.googleapis.json.GoogleJsonResponseException
import com.google.api.client.http.InputStreamContent
import com.google.api.client.json.gson.GsonFactory
import com.google.api.services.drive.Drive
import com.google.api.services.drive.DriveScopes
import com.google.api.services.drive.model.File
import com.google.auth.http.HttpCredentialsAdapter
import com.google.auth.oauth2.GoogleCredentials
import org.springframework.stereotype.Component
import ru.classbase.dbdumper.AppProperties
import ru.classbase.dbdumper.getFileExtension
import ru.classbase.dbdumper.getMimeType
import ru.classbase.dbdumper.model.DbMetaData
import ru.classbase.dbdumper.model.DumpContext
import ru.classbase.dbdumper.model.StorageContext
import ru.classbase.dbdumper.model.StorageType
import java.io.InputStream
import java.nio.file.Files


@Component
class GoogleDriveStorage(private val props: AppProperties) : DumpStorage {
    private val folderMimeType = "application/vnd.google-apps.folder"
    private val gdriveScope = DriveScopes.DRIVE
    private val jsonFactory = GsonFactory.getDefaultInstance()
    private val httpTransport = GoogleNetHttpTransport.newTrustedTransport()
    private val service = Drive.Builder(httpTransport, jsonFactory, HttpCredentialsAdapter(getCredentials()))
        .setApplicationName(props.gdrive.applicationName)
        .build()

    override val supportedStorage = StorageType.GDRIVE

    override fun uploadFrom(meta: DbMetaData, ctx: StorageContext) {
        if (props.gdrive.folderId.isNullOrEmpty()) {
            throw IllegalArgumentException("GDRIVE folder must not be null")
        }

        val folder = getFolder(props.gdrive.folderId)
        val ext = getFileExtension(props.outputFormat, props.compressionType)

        val fileMetadata = File()
        fileMetadata.setName("${ctx.tableName}.$ext")
        fileMetadata.setParents(listOf(folder.id))

        val mediaContent = InputStreamContent(getMimeType(props.outputFormat, props.compressionType), ctx.input)

        try {
            val file = service.files().create(fileMetadata, mediaContent)
                .setFields("id, parents")
                .execute()
            println("uploaded File ID: " + file.id)
            //return file
        } catch (e: GoogleJsonResponseException) {
            // TODO(developer) - handle error appropriately
            System.err.println("Unable to upload file: " + e.details)
            throw e
        }

/*
        val result = service.files().list()
            .setPageSize(10)
            .setFields("nextPageToken, files(id, name, capabilities)")
            .execute()


        val files = result.files

        if (files == null || files.isEmpty()) {
            println("No files found.")
        } else {
            println("Files:")
            for (file in files) {
                System.out.printf("%s (%s)\n", file.name, file.id)
                //service.files().delete(file.id).execute()
            }
        }
*/


    }


    private fun getFolder(fileId: String): File {

        try {
            val folder = service.files().get(fileId)
                .setFields("id, name, mimeType, capabilities")
                .execute()

            if (folder.mimeType != folderMimeType) {
                throw IllegalStateException("Указанный ресурс не является папкой")
            }

            if (!folder.capabilities.canAddChildren) {
                throw IllegalStateException("Нет прав на запись в указанную папку")
            }

            return folder
        } catch (e: GoogleJsonResponseException) {
            println(e.message)
            throw e
        }

    }

    private fun getCredentials(): GoogleCredentials {
        return GoogleCredentials
            .fromStream(Files.newInputStream(props.gdrive.credentialsFilePath))
            .createScoped(listOf(gdriveScope))
    }

}